home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CD ROM Paradise Collection 4
/
CD ROM Paradise Collection 4 1995 Nov.iso
/
clang
/
tkern091.zip
/
SRC
/
PROCESS.C
< prev
next >
Wrap
Text File
|
1994-03-06
|
18KB
|
837 lines
/*
* This file forms part of "TKERN" - "Troy's Kernel for Windows".
*
* Copyright (C) 1994 Troy Rollo <troy@cbme.unsw.EDU.AU>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* This module handles task and process management.
*
* Tasks are Windows objects, identified by a task handle. Task handles
* can be reused without us waiting for them, so we can't use them to
* track parent-child relationships or to impliment the wait calls.
*
* Processes are tkern only objects which overcome these limitations.
* Every task is assigned a process number when it enters the system.
* When it leaves the system, if its parent is a tkern process, and
* it was created from tkern_exec, a zombie process is created, for which
* the parent mus wait.
*/
#include <windows.h>
#include <toolhelp.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include <alloc.h>
#include <stdarg.h>
#include <errno.h>
#include <sys/tfile.h>
#include <sys/task.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/tkern.h>
struct tk_process _process[N_TKPROCS];
struct tk_process _zombies[N_TKPROCS];
struct task _tasks[TNTASK];
int nTasks = 0; // Doesn't count fledgelings
static int nProcesses = 0;
static int nZombies = 0;
static struct task *ptNext = 0;
static struct task *ptParent = 0;
static HTASK htaskParent = 0;
static short pidParent = 0;
static short pidChild = 0;
static short pidCurrent;
static short nPIDNext = 3; // The first PID is 2, but that's fixed up
// in WinMain.
static void task_is_dead( int iTask);
struct task *
GetTaskInfo(void)
{
HTASK hTask;
int i;
hTask = GetCurrentTask();
for (i = 0; i < TNTASK; i++)
{
if (_tasks[i].hTask == hTask)
{
while (_tasks[i].iFledgeling != -1)
i = _tasks[i].iFledgeling;
return &_tasks[i];
}
}
for (i = 0; i < TNTASK; i++)
{
if (!_tasks[i].hTask)
{
_tasks[i].hTask = hTask;
return &_tasks[i];
}
}
return 0;
}
void
inc_pid(short *pid)
{
if (*pid == 32767)
*pid = 2;
else
(*pid)++;
}
/*
* aiExecErrors translates between WinExec errors and errno errors
*/
static int aiExecErrors[] =
{
ENOMEM,
EFAULT,
ENOENT,
ENOENT,
EFAULT,
ENOEXEC,
ENOEXEC,
EFAULT,
EFAULT,
EFAULT,
ENOEXEC,
ENOEXEC,
ENOEXEC,
ENOEXEC,
ENOEXEC,
ENOEXEC,
ETXTBSY,
ETXTBSY,
ENOEXEC,
EFAULT,
EFAULT,
EFAULT,
EFAULT,
EFAULT,
EFAULT,
EFAULT,
EFAULT,
EFAULT,
EFAULT,
EFAULT,
EFAULT,
EFAULT,
EFAULT
};
/* A normal POSIX exec would require pchPath, vaArgs and vaEnv.
* TKERN's also takes "pchCmdLine", the original command line.
* this is used by programs that don't use TKERN, and is the
* value used for WinExec, with pchPath prepended. If the program
* uses TKERN, it will call in to register, and then we tell it
* about its real arguments and environment.
*
* Yes, we inherit environment too.
*/
short far _export
tkern_exec( char const *pchPath,
va_list vaArgs,
va_list vaEnv,
char const *pchCmdLine)
{
struct task *pt;
char *pchCommand;
int iNew;
pt = GetTaskInfo();
if (pt->nFlags & TF_EXEC) /* Attempt to recursively exec */
{
pt->nError = EAGAIN;
return -1;
}
pt->nFlags |= TF_EXEC;
Copy_Array(&pt->argv, (char **) vaArgs);
Copy_Array(&pt->envp, (char **) vaEnv);
pchCommand = (char *) malloc(strlen(pchPath) + strlen(pchCmdLine) + 2);
strcpy(pchCommand, pchPath);
if (pchCmdLine)
{
strcat(pchCommand, " ");
strcat(pchCommand, pchCmdLine);
}
while (ptNext)
GetMessages(pt); /* We only allow one exec at any one time */
ptNext = pt;
for (ptParent = pt;
ptParent->hTask == HTASK_FLEDGELING;
ptParent = _tasks + ptParent->iParent);
htaskParent = ptParent->hTask;
_tasks[pt->iParent].iFledgeling = -1;
pt->iParent = 0; /* We don't keep this because the parent
* may die.
*/
pidParent = ptParent->pid;
pidChild = 0;
if ((iNew = WinExec(pchCommand, SW_SHOW)) < 32)
{
ptParent->nError = aiExecErrors[iNew];
ptParent->iFledgeling = -1;
ptNext = 0;
task_is_dead(pt - _tasks);
htaskParent = 0;
pt->nFlags &= ~TF_EXEC;
pt->nError = ENOENT;
tkern_wakeup_call();
return -1;
}
free(pchCommand);
ptParent->nChildren++;
ptNext = 0;
htaskParent = 0;
tkern_wakeup_call();
pt->nFlags &= ~TF_EXEC;
if (pt->hTask == HTASK_FLEDGELING)
{
ptParent->iFledgeling = -1;
task_is_dead(pt - _tasks);
}
return pidChild;
}
/*
* The three forms of wait.
*
* First, the original wait, which waits unconditionally until the child
* dies.
*
* Second, wait3, which takes flags. It is also supposed to return
* resource usage information but we don't keep that.
*
* Third, waitpid, the POSIX wait. This is to be preferred over wait3.
*/
short far _export
tkern_wait(union wait *wstatus)
{
int i;
short pid;
struct task *pt;
struct task *ptParent;
pt = GetTaskInfo();
/* Fledgelings cannot be parents */
for (ptParent = pt;
ptParent->hTask == HTASK_FLEDGELING;
ptParent = _tasks + ptParent->iParent);
/* If the parent is childless, return immediately */
if (!ptParent->nChildren)
return 0;
/* Wait until the parent has dead children */
while (!ptParent->nZombies)
GetMessages(pt);
/* Reap the first dead child we encounter and return its pid */
for (i = 0; i < nZombies; i++)
{
if (_zombies[i].pidParent == ptParent->pid)
{
pid = _zombies[i].pid;
wstatus->w_status = 0;
wstatus->w_retcode = _zombies[i].nRetCode;
if (i < nZombies - 1)
_zombies[i] = _zombies[nZombies - 1];
nZombies--;
ptParent->nZombies--;
ptParent->nChildren--;
return pid;
}
}
return 0;
}
short far _export
tkern_wait3(union wait *wstatus,
int nFlags,
struct rusage *pZero)
{
int i;
short pid;
struct task *pt;
struct task *ptParent;
pt = GetTaskInfo();
if (pZero)
{
pt->nError = EINVAL;
return -1;
}
/* Fledgelings cannot be parents */
for (ptParent = pt;
ptParent->hTask == HTASK_FLEDGELING;
ptParent = _tasks + ptParent->iParent);
/* If the parent is childless, return immediately */
if (!ptParent->nChildren)
return 0;
/* If the WNOHANG flag is supplied, and there are no
* zombies, return immediately
*/
if (!ptParent->nZombies && (nFlags & WNOHANG))
return 0;
/* Since we don't have BSD jobs, we can't use WUNTRACED */
/* Wait until the parent has dead children */
while (!ptParent->nZombies)
GetMessages(pt);
/* Reap the first dead child we encounter and return its pid */
for (i = 0; i < nZombies; i++)
{
if (_zombies[i].pidParent == ptParent->pid)
{
pid = _zombies[i].pid;
wstatus->w_status = 0;
wstatus->w_retcode = _zombies[i].nRetCode;
if (i < nZombies - 1)
_zombies[i] = _zombies[nZombies - 1];
nZombies--;
ptParent->nZombies--;
ptParent->nChildren--;
return pid;
}
}
return 0;
}
short far _export
tkern_waitpid( int pid,
union wait *wstatus,
int nFlags)
{
int i;
struct task *pt;
struct task *ptParent;
BOOL bZombie;
int iEntry;
struct tk_process *pProc;
int nLastZombies = -1;
pt = GetTaskInfo();
/* Fledgelings cannot be parents */
for (ptParent = pt;
ptParent->hTask == HTASK_FLEDGELING;
ptParent = _tasks + ptParent->iParent);
/* If the parent is childless, return immediately */
if (!ptParent->nChildren)
return 0;
while (1)
{
/* Find the process entry for this child */
if (pid)
{
if (ptParent->nZombies != nLastZombies)
{
iEntry = -1;
for (i = 0; iEntry == -1 && i < nProcesses; i++)
{
if (_process[i].pid == pid)
{
iEntry = i;
bZombie = FALSE;
pProc = &_process[i];
}
}
for (i = 0; iEntry == -1 && i < nZombies; i++)
{
if (_zombies[i].pid == pid)
{
iEntry = i;
bZombie = TRUE;
pProc = &_zombies[i];
}
}
if (pProc->pidParent != ptParent->pid)
{
pt->nError = EACCES;
return -1;
}
nLastZombies = ptParent->nZombies;
if (iEntry == -1)
{
pt->nError = EFAULT;
return -1;
}
if (!bZombie && (nFlags & WNOHANG))
return 0;
if (bZombie)
{
wstatus->w_status = 0;
wstatus->w_retcode = _zombies[iEntry].nRetCode;
nZombies--;
if (iEntry < nZombies)
_zombies[i] = _zombies[nZombies];
ptParent->nZombies--;
ptParent->nChildren--;
return pid;
}
}
else if (!ptParent->nZombies && (nFlags & WNOHANG))
{
return 0;
}
}
else if (ptParent->nZombies)
{
for (i = 0; i < nZombies; i++)
{
if (_zombies[i].pidParent == ptParent->pid)
{
pid = _zombies[i].pid;
wstatus->w_status = 0;
wstatus->w_retcode = _zombies[i].nRetCode;
if (i < nZombies - 1)
_zombies[i] = _zombies[nZombies - 1];
nZombies--;
ptParent->nZombies--;
ptParent->nChildren--;
return pid;
}
}
}
else if (nFlags & WNOHANG)
{
return 0;
}
/* Wait until the parent has dead children */
GetMessages(pt);
}
}
/* tkern_fork only handles the internal (to tkern) part of fork().
* the Throw/Catch functions must be handled in the child program.
* consequently, fork is represented as a macro.
*/
int far _export
tkern_fork(void)
{
struct task *pt;
int i, j;
pt = GetTaskInfo();
for (i = 0; i < TNTASK; i++)
{
if (!_tasks[i].hTask)
break;
}
if (i == TNTASK)
{
pt->nError = ENOMEM;
return -1;
}
pt->iFledgeling = i;
_tasks[i].hTask = HTASK_FLEDGELING;
_tasks[i].iParent = (pt - _tasks);
memcpy(_tasks[i].files, pt->files, sizeof(_tasks[i].files));
for (j = 0; j < UFILE_MAX; j++)
{
if (_tasks[i].files[j] != -1)
_files[_tasks[i].files[j]].tf_cnt++;
}
return 0; /* When we return, we are in the "child" process */
}
int far _export
tkern_total_zombies(void)
{
return nZombies;
}
int far _export
tkern_list_zombies( struct tk_process *pList,
int nEntries)
{
if (nZombies < nEntries)
nEntries = nZombies;
memcpy(pList, _zombies, sizeof(*pList) * nEntries);
return nEntries;
}
/* Note that because tkern_get_process takes an HTASK, it can
* only return an active process, not a zombied one
*/
int far _export
tkern_get_process( HTASK hTask,
struct tk_process *tk_proc)
{
int i;
for (i = 0; i < nProcesses; i++)
{
if (_process[i].hTask == hTask)
{
*tk_proc = _process[i];
return _process[i].pid;
}
}
return 0;
}
void far _export
tkern_register_program( int *argc,
char ***argv,
char ***envp)
{
int i;
struct task *pt;
TASKENTRY te;
TaskFindHandle(&te, GetCurrentTask());
if (htaskParent &&
(!te.hTaskParent ||
htaskParent == te.hTaskParent))
{
/* Because of the way tkern_exec() is written,
* the only way this can be true is if this
* is the task spawned from the tkern_exec()
*/
pt = ptNext;
pt->hTask = GetCurrentTask();
for (i = 0; pt->argv[i]; i++);
*argc = i;
*argv = pt->argv;
*envp = pt->envp;
pt->pid = pidCurrent;
}
else
{
pt = GetTaskInfo();
if (pt->argv)
{
for (i = 0; *pt->argv; i++);
*argc = i;
*argv = pt->argv;
*envp = pt->envp;
}
else
{
*argc = 0;
*argv = 0;
*envp = 0;
}
pt->pid = pidCurrent;
}
/* We need to flush all messages in the first task because if
* that task exits without having yielded, TKFMANGR will miss
* the exit notification.
*/
if (!nTasks)
FlushMessages();
nTasks++;
while (!hwndManager)
GetMessages(pt);
}
static void
task_is_dead( int iTask)
{
int iFile;
if (_tasks[iTask].iFledgeling != -1)
task_is_dead(_tasks[iTask].iFledgeling);
for (iFile = 0; iFile < UFILE_MAX; iFile++)
if (_tasks[iTask].files[iFile] != -1)
internal_close(iFile, iTask);
if (_tasks[iTask].argv)
{
free(_tasks[iTask].argv);
_tasks[iTask].argv = 0;
}
if (_tasks[iTask].envp)
{
free(_tasks[iTask].envp);
_tasks[iTask].envp = 0;
}
_tasks[iTask].hTask = 0;
}
#pragma argsused
void far _export
tkern_program_started(HTASK htaskNew)
{
short iEntry, i;
short nPID;
BOOL bOK;
if (nProcesses == N_TKPROCS) // Should be impossible
return; // Well what else can we do?
iEntry = nProcesses++;
do
{
nPID = nPIDNext;
bOK = TRUE;
for (i = 0; i < nProcesses; i++)
{
if (_process[i].pid == nPID)
{
bOK = FALSE;
break;
}
}
if (bOK)
{
for (i = 0; i < nZombies; i++)
{
if (_zombies[i].pid == nPID)
{
bOK = FALSE;
break;
}
}
}
inc_pid(&nPIDNext);
} while (!bOK);
_process[iEntry].pid = nPID;
_process[iEntry].hTask = htaskNew;
if (ptNext && !pidChild)
{
_process[iEntry].pidParent = pidParent;
_process[iEntry].hTaskParent = htaskParent;
pidChild = nPID;
_process[iEntry].iParent = ptParent - _tasks;
}
else
{
_process[iEntry].pidParent = 1;
_process[iEntry].hTaskParent = 0;
_process[iEntry].iParent = -1;
}
pidCurrent = nPID;
}
void far _export
tkern_program_dead( HTASK htaskCorpse,
int nRetCode)
{
int i;
short nPID = 0;
/* First, clean up the tkern data on the task, along with
* all open files.
*/
/* MessageBox(0, "Phew. Has somebody died in here?", 0, MB_OK); */
for (i = 0; i < TNTASK; i++)
{
if (_tasks[i].hTask == htaskCorpse)
{
task_is_dead(i);
nTasks--;
if (!nTasks)
SendMessage(hwndManager, TKWM_ALLDONE, 0, 0);
break;
}
}
/* Next, look for any children, and the process itself, in the process
* list.
*/
for (i = 0; i < nProcesses; i++)
{
/* Change any orphaned processes to pidParent = 1, hTaskParent = 0 */
if (_process[i].hTaskParent == htaskCorpse)
{
/* A process has become orphaned */
_process[i].hTaskParent = 0;
_process[i].pidParent = 1;
_process[i].iParent = -1;
}
if (_process[i].hTask == htaskCorpse)
{
/* This is the process entry for this task */
nPID = _process[i].pid;
/* If there is room in the zombies table, and the process
* has a living parent, copy the process entry to the zombies
* table, and issue a wakeup call for any processes waiting
* for children.
*
* note that if the process has no parents, failing to copy
* it to the zombie table automatically causes it to be reaped.
*/
if (nZombies < N_TKPROCS && _process[i].iParent != -1)
{
_zombies[nZombies] = _process[i];
_zombies[nZombies].nRetCode = nRetCode;
_tasks[_process[i].iParent].nZombies++;
nZombies++;
tkern_wakeup_call();
}
nProcesses--;
if (i != nProcesses)
{
_process[i] = _process[nProcesses];
i--;
}
}
}
/* Search for any zombie children of the current process and reap them */
for (i = 0; i < nZombies; i++)
{
if (_zombies[i].pidParent == nPID)
{
nZombies--;
if (i != nZombies)
{
_zombies[i] = _zombies[nZombies];
i--;
}
}
}
}
void
process_init(void)
{
TASKENTRY te;
HTASK htaskNow;
int i, j;
for (i = 0; i < TNTASK; i++)
{
_tasks[i].hTask = 0;
_tasks[i].argv = 0;
_tasks[i].envp = 0;
_tasks[i].pid = 0;
_tasks[i].nChildren = 0;
_tasks[i].nZombies = 0;
_tasks[i].iFledgeling = -1;
for (j = 0; j < UFILE_MAX; j++)
_tasks[i].files[j] = -1;
}
htaskNow = GetCurrentTask();
te.dwSize = sizeof(TASKENTRY);
TaskFirst(&te);
memset(_process, 0, sizeof(_process));
memset(_zombies, 0, sizeof(_zombies));
do
{
if (htaskNow == te.hTask)
pidCurrent = nPIDNext;
_process[nProcesses].pid = nPIDNext++;
_process[nProcesses].pidParent = 1;
_process[nProcesses].hTaskParent = 0;
_process[nProcesses].hTask = te.hTask;
_process[nProcesses].iParent = -1;
nProcesses++;
} while (TaskNext(&te));
}
int far _export
tkern_kill( int pid,
int nSignal)
{
int i;
struct task *pt;
pt = GetTaskInfo();
for (i = 0; i < nProcesses; i++)
{
if (_process[i].pid == pid)
{
switch(nSignal)
{
case 0:
break;
case 1:
case 2:
case 9:
case 14:
TerminateApp(_process[i].hTask, NO_UAE_BOX);
break;
default:
TerminateApp(_process[i].hTask, UAE_BOX);
break;
}
return 0;
}
}
for (i = 0; i < nZombies; i++)
{
if (_process[i].pid == pid)
return 0; /* Can't kill the undead */
}
pt = GetTaskInfo();
pt->nError = EFAULT;
return -1;
}